package com.muyu.system.danmo.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.muyu.system.danmo.domain.AliPayConfig;
import com.muyu.system.danmo.domain.PayOrder;
import com.muyu.system.danmo.domain.RefundOrder;
import com.muyu.system.danmo.dto.PayServiceDTO;
import com.muyu.system.danmo.dto.PaymentCallBackResult;
import com.muyu.system.danmo.dto.RefundServiceDTO;
import com.muyu.common.core.enums.PayChannel;
import com.muyu.common.core.enums.PayStatus;
import com.muyu.system.danmo.service.AliPayConfigService;
import com.muyu.system.danmo.service.PayChannelService;
import com.muyu.system.danmo.service.PayOrderService;
import com.muyu.system.danmo.util.AliPayConstant;
import com.muyu.system.danmo.vo.PayResultVO;
import com.muyu.system.danmo.vo.RefundResultServiceVO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;

import static com.muyu.common.core.constant.TopicConstant.COMMON_TOPIC_NORMAL;
import static com.muyu.system.danmo.util.TagConstant.PAY_NOTIFY_TAG;

@Service("AliPayService")
@Slf4j
@RequiredArgsConstructor
public class AliPayService implements PayChannelService {

    @Value("${alipay_notify_url}")
    private String notifyUrl;


    private final AliPayConfigService aliPayConfigService;


    private final Map<String, AlipayClient> clientMap = new HashMap<>();

    private static final String SERVER_URL = "https://openapi.alipay.com/gateway.do";

    /**
     * 时间格式(yyyy-MM-dd HH:mm:ss)
     */
    public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    private final RocketMQTemplate rocketMQTemplate;

    private final PayOrderService payOrderService;


    @Override
    public PayResultVO pay(PayServiceDTO payServiceDTO, PayOrder payOrder) {
        log.info("进入支付宝支付：{} ==== {}", payServiceDTO, payOrder);
        // 获取配置
        AliPayConfig aliPayConfig = aliPayConfigService.lambdaQuery().eq(AliPayConfig::getMchCode, payOrder.getMchCode()).one();
        PayResultVO payResultVO = new PayResultVO();
        payResultVO.setBizOrderNo(payOrder.getBizOrderNo());
        payResultVO.setOrderNo(payOrder.getOrderNo());
        switch (payServiceDTO.getPayMethod()){
            case ALI_APP:
                // 组装参数
                AlipayTradeAppPayRequest alipayTradeAppPayRequest = getAlipayTradeAppPayRequest(payServiceDTO, payOrder);
                String payInfo = appResponse(payServiceDTO, alipayTradeAppPayRequest, aliPayConfig);
                payResultVO.setPayInfo(payInfo);
                break;
            default:
                throw new RuntimeException("不支持的支付方式");
        }
        return payResultVO;
    }

    @Override
    public ResponseEntity.BodyBuilder payNotify(HttpServletRequest request, PayChannel payChannel, String mchCode) {
        // 获取配置
        AliPayConfig aliPayConfig = aliPayConfigService.lambdaQuery().eq(AliPayConfig::getMchCode, mchCode).one();
        if (request.getParameterMap() != null) {
            Map<String, String> map = getParams(request.getParameterMap());
            Boolean flag = null;
            try {
                flag = AlipaySignature.rsaCheckV1(map, aliPayConfig.getAlipayPublicKey(), "UTF-8", "RSA2");
            } catch (AlipayApiException e) {
                log.error("支付宝回调验签失败1：{}", e.getMessage());
                throw new RuntimeException(e);
            }
            if(flag){
                PaymentCallBackResult result = new PaymentCallBackResult();
                result.setOrderNo(map.get("out_trade_no"));
                switch (map.get("trade_status")){
                    case AliPayConstant.TRADE_SUCCESS:
                        result.setStatus(PayStatus.SUCCESS);
                        result.setTransactionId(map.get("trade_no"));
                        result.setOpenId(map.get("buyer_id"));
                        String timeStr = map.get("gmt_close");
                        if (StrUtil.isNotBlank(timeStr)) {
                            result.setCompletedTime(LocalDateTimeUtil.parse(timeStr, DATE_TIME_PATTERN));
                        } else {
                            result.setCompletedTime(LocalDateTime.now());
                        }
                        result.setNotifyTime(LocalDateTime.now());
                        break;
                    case AliPayConstant.WAIT_BUYER_PAY:
                        result.setStatus(PayStatus.PENDING);
                        break;
                    default:
                        result.setStatus(PayStatus.FAIL);
                }
                // 获取业务方的订单号
                PayOrder one = payOrderService.lambdaQuery().eq(PayOrder::getOrderNo, result.getOrderNo()).select(PayOrder::getBizOrderNo).one();
                if (one != null) {
                    result.setBizOrderNo(one.getBizOrderNo());
                }
                SendResult sendResult = rocketMQTemplate.syncSend(COMMON_TOPIC_NORMAL + ":" + PAY_NOTIFY_TAG, JSONUtil.toJsonStr(result));
                log.info("支付宝支付回调完整结果：{} == mq:{}",JSONUtil.toJsonStr(result),  sendResult.getMsgId());

            }else {
                log.error("支付宝回调验签失败2!");
                throw new RuntimeException("支付宝回调验签失败2");
            }
        }
        return ResponseEntity.status(HttpStatus.OK);
    }

    @Override
    public ResponseEntity.BodyBuilder refundPayNotify(HttpServletRequest request, PayChannel payChannel, String mchCode) {
        return null;
    }

    @Override
    public PaymentCallBackResult queryOrder(String mchCode, String orderNo) {
        PaymentCallBackResult paymentCallBackResult = new PaymentCallBackResult();
        paymentCallBackResult.setOrderNo(orderNo);
        // 获取配置
        AliPayConfig aliPayConfig = aliPayConfigService.lambdaQuery().eq(AliPayConfig::getMchCode, mchCode).one();
        AlipayClient client = getClient(mchCode, aliPayConfig);
        // 构造请求参数以调用接口
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        model.setOutTradeNo(orderNo);
        request.setBizModel(model);
        AlipayTradeQueryResponse response = null;
        try {
            log.info("支付宝查询订单参数：{}", JSONUtil.toJsonStr(request));
            response = client.execute(request);
            log.info("支付宝查询订单响应：{}", JSONUtil.toJsonStr(response));
        } catch (AlipayApiException e) {
            log.error("支付宝查询订单失败：{}", e.getMessage(),e);
            throw new RuntimeException(e);
        }
        if (response.isSuccess()) {
            if (AliPayConstant.TRADE_SUCCESS.equals(response.getTradeStatus()) || AliPayConstant.TRADE_FINISHED.equals(response.getTradeStatus())) {
                paymentCallBackResult.setStatus(PayStatus.SUCCESS);
                paymentCallBackResult.setTransactionId(response.getTradeNo());
                paymentCallBackResult.setOpenId(response.getBuyerUserId());
                paymentCallBackResult.setCompletedTime(LocalDateTimeUtil.of(response.getSendPayDate()));
                paymentCallBackResult.setNotifyTime(DateUtil.date().toLocalDateTime());
            } else if (AliPayConstant.WAIT_BUYER_PAY.equals(response.getTradeStatus())) {
                paymentCallBackResult.setOpenId(response.getBuyerUserId());
                paymentCallBackResult.setStatus(PayStatus.PENDING);
            } else if (AliPayConstant.TRADE_CLOSED.equals(response.getTradeStatus())) {
                paymentCallBackResult.setOpenId(response.getBuyerUserId());
                paymentCallBackResult.setStatus(PayStatus.TIMEOUT);
            } else {
                paymentCallBackResult.setMessage(response.getSubMsg());
                paymentCallBackResult.setStatus(PayStatus.FAIL);
            }
        } else {
            log.error("支付宝查询订单失败2");
            if(response.getSubCode().equals("ACQ.TRADE_NOT_EXIST")){
                paymentCallBackResult.setStatus(PayStatus.FAIL);
                paymentCallBackResult.setMessage(response.getSubMsg());
                return paymentCallBackResult;
            }
            throw new RuntimeException(response.getSubMsg());
        }
        return paymentCallBackResult;
    }

    @Override
    public RefundResultServiceVO refund(RefundServiceDTO refundDTO, RefundOrder refundOrder) {
        RefundResultServiceVO result = new RefundResultServiceVO();
        result.setRefundOrderNo(refundOrder.getRefundOrderNo());
        result.setRefundBizOrderNo(refundOrder.getRefundBizOrderNo());
        // 获取请求客户端
        AliPayConfig aliPayConfig = aliPayConfigService.lambdaQuery().eq(AliPayConfig::getMchCode, refundOrder.getMchCode()).one();
        AlipayClient client = getClient(refundOrder.getMchCode(), aliPayConfig);
        // 构造请求参数以调用接口
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        BigDecimal amountYuan = BigDecimal.valueOf(refundOrder.getRefundAmount())
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_DOWN);
        model.setRefundAmount(amountYuan.toString());
        model.setOutTradeNo(refundOrder.getOrderNo());
        model.setOutRequestNo(refundOrder.getRefundOrderNo());
        request.setBizModel(model);
        AlipayTradeRefundResponse response = null;
        try {
            response = client.execute(request);
        } catch (AlipayApiException e) {
            log.error("支付宝退款失败：{}", e.getMessage(),e);
            throw new RuntimeException(e);
        }

        if(!response.isSuccess()){
            log.error("支付宝退款请求失败:{}",JSONUtil.toJsonStr(response));
            throw new RuntimeException("支付宝退款请求失败");
        }
        log.info("支付宝退款响应：{}", JSONUtil.toJsonStr(response));
        if (response.getFundChange().equals(AliPayConstant.YES)) {
            result.setStatus(PayStatus.SUCCESS);
            result.setTransactionId(response.getTradeNo());
            result.setCompletedTime(LocalDateTime.now());
        } else if (response.getFundChange().equals(AliPayConstant.NO)) {
            result.setStatus(PayStatus.PENDING);
        } else {
            result.setStatus(PayStatus.FAIL);
        }
        return result;
    }

    @Override
    public RefundResultServiceVO refundQueryOrder(RefundOrder refundOrder) {
        RefundResultServiceVO refundResultServiceVO = new RefundResultServiceVO();
        refundResultServiceVO.setPayChannel(refundOrder.getPayChannel());
        refundResultServiceVO.setRefundOrderNo(refundOrder.getRefundOrderNo());
        refundResultServiceVO.setRefundBizOrderNo(refundOrder.getRefundBizOrderNo());
        // 获取请求客户端
        AliPayConfig aliPayConfig = aliPayConfigService.lambdaQuery().eq(AliPayConfig::getMchCode, refundOrder.getMchCode()).one();
        AlipayClient client = getClient(refundOrder.getMchCode(), aliPayConfig);
        // 构造请求参数以调用接口
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
        model.setOutRequestNo(refundOrder.getRefundOrderNo());
        model.setOutTradeNo(refundOrder.getOrderNo());
        String[] queryOptions = new String[]{"gmt_refund_pay"};
        model.setQueryOptions(Arrays.asList(queryOptions));
        request.setBizModel(model);
        AlipayTradeFastpayRefundQueryResponse response = null;
        try {
            response = client.execute(request);
        } catch (AlipayApiException e) {
            log.error("支付宝退款查询失败：{}", e.getMessage(),e);
            throw new RuntimeException(e);
        }
        if(!response.isSuccess()){
            log.error("支付宝退款查询失败!!!!");
            throw new RuntimeException("支付宝退款查询失败!!!!");
        }

        if (AliPayConstant.REFUND_SUCCESS.equals(response.getRefundStatus())) {
            refundResultServiceVO.setTransactionId(response.getTradeNo());
            refundResultServiceVO.setStatus(PayStatus.SUCCESS);
            Date a = response.getGmtRefundPay();
            if (a != null) {
                refundResultServiceVO.setCompletedTime(LocalDateTimeUtil.of(a));
            }
        } else {
            refundResultServiceVO.setStatus(PayStatus.FAIL);
        }
        return refundResultServiceVO;
    }

    @Override
    public void close(PayOrder payOrder) {
        // 获取请求客户端
        AliPayConfig aliPayConfig = aliPayConfigService.lambdaQuery().eq(AliPayConfig::getMchCode, payOrder.getMchCode()).one();
        AlipayClient client = getClient(payOrder.getMchCode(), aliPayConfig);
        // 构造请求参数以调用接口
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        AlipayTradeCloseModel model = new AlipayTradeCloseModel();
        // 设置订单支付时传入的商户订单号
        model.setOutTradeNo(payOrder.getOrderNo());
        request.setBizModel(model);
        AlipayTradeCloseResponse response = null;
        try {
            response = client.execute(request);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        log.info("关闭支付宝订单response:{}",JSONUtil.toJsonStr(response));
        if (response.isSuccess()) {
            log.info("支付宝订单关闭调用成功！");
        } else if("ACQ.TRADE_NOT_EXIST".equals(response.getSubCode())){
            log.info("支付宝订单应经关闭了！");
        }else {
            throw new RuntimeException("支付宝关闭订单调用失败");
        }
        // 查询订单状态是否时关闭
        PaymentCallBackResult paymentCallBackResult = queryOrder(payOrder.getMchCode(), payOrder.getOrderNo());
        if(!paymentCallBackResult.getStatus().equals(PayStatus.FAIL)){
            throw new RuntimeException("支付宝订单没有关闭！当前状态时：" + paymentCallBackResult.getStatus().getName());
        }
    }


    private Map<String, String> getParams(Map<String, String[]> requestParams) {
        Map<String, String> params = new HashMap<String, String>(10);
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = iter.next();
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        return params;
    }


    /**
     * app请求
     * @param payServiceDTO
     * @param alipayTradeAppPayRequest
     */
    private String appResponse(PayServiceDTO payServiceDTO, AlipayTradeAppPayRequest alipayTradeAppPayRequest, AliPayConfig aliPayConfig){
        // 获取阿里客户端
        alipayTradeAppPayRequest.setNotifyUrl(StrUtil.format(notifyUrl, payServiceDTO.getMchCode(),payServiceDTO.getPayMethod().getPayChannel().toString()));
        AlipayClient client = getClient(payServiceDTO.getMchCode(), aliPayConfig);
        AlipayTradeAppPayResponse alipayTradeAppPayResponse = null;
        // 发起请求
        try {
            alipayTradeAppPayResponse = client.sdkExecute(alipayTradeAppPayRequest);
        } catch (AlipayApiException e) {
            log.error("支付宝app请求失败：{}", e.getMessage(),e);
            throw new RuntimeException(e);
        }
        if(!alipayTradeAppPayResponse.isSuccess()){
            log.error("支付宝app请求返回失败：{}", alipayTradeAppPayResponse.getMsg());
            throw new RuntimeException("支付宝app请求返回失败");
        }
        log.info("支付宝app请求成功返回值：{}", JSONUtil.toJsonStr(alipayTradeAppPayResponse));

        return alipayTradeAppPayResponse.getBody();
    }


    /**
     * 获取阿里请求客户端
     * @param mchCode
     * @param aliPayConfig
     */
    @SneakyThrows
    private AlipayClient getClient(String mchCode, AliPayConfig aliPayConfig){
        AlipayClient alipayClient = clientMap.get(mchCode);
        if(alipayClient == null){
            AlipayConfig alipayConfig = new AlipayConfig();
            alipayConfig.setServerUrl(SERVER_URL);
            alipayConfig.setAppId(aliPayConfig.getAppId());
            alipayConfig.setPrivateKey(aliPayConfig.getPrivateKey());
            alipayConfig.setFormat("json");
            alipayConfig.setAlipayPublicKey(aliPayConfig.getAlipayPublicKey());
            alipayConfig.setCharset("UTF-8");
            alipayConfig.setSignType("RSA2");
            AlipayClient client = new DefaultAlipayClient(alipayConfig);
            clientMap.put(mchCode,client);
        }
        return clientMap.get(mchCode);
    }


    /**
     * 组装app的请求参数
     * @param payServiceDTO
     * @param payOrder
     * @return
     */
    private AlipayTradeAppPayRequest getAlipayTradeAppPayRequest(PayServiceDTO payServiceDTO, PayOrder payOrder){
        // 构造请求参数以调用接口
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setOutTradeNo(payOrder.getOrderNo());
        model.setTotalAmount(new BigDecimal(payOrder.getTotalAmount().toString())
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_DOWN).toString());
        model.setSubject(payServiceDTO.getDescription());
        if(null == payServiceDTO.getTimeExpire()){
            payServiceDTO.setTimeExpire(DateUtil.offsetMinute(DateUtil.date(), 10).toLocalDateTime());
        }
        String date = DateUtil.format(payServiceDTO.getTimeExpire(), DATE_TIME_PATTERN);
        model.setTimeExpire(date);
        request.setBizModel(model);
        log.info("支付宝app请求参数：{}", JSONUtil.toJsonStr(request));
        return request;
    }
}
